/*
 * HttpHelper.cpp
 *
 *  Created on: 23.9.2011
 *      Author: lis1
 */
#include <stdio.h>

#include "HttpHelper.h"
#include "AppLogger.h"
#include <string>
#include <sstream>
#include <iostream>

#include <stdlib.h>

#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>
#include <netinet/in.h>
#include <arpa/inet.h>

#include <string.h>

using namespace std;

static AppLogger * logger = AppLogger::getInstance("HttpHelper");

static char _httpReponseBuffer[64 * 1024];

const string CRLF = "\r\n";

int HttpHelper::create_tcp_socket() {
	int sock;
	if ((sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0) {
		logger->error("Can't create TCP socket");
		perror("Can't create TCP socket");
		exit(1);
	}
	return sock;
}

HttpHelper::HttpHelper() {
}

HttpHelper::~HttpHelper() {
}


int HttpHelper::extractIcyMetaInt(char * httpResponseHeaders)
{
	const char * headerName = "icy-metaint:";
	char * pch = strstr(httpResponseHeaders, headerName);
	if (NULL == pch){
		return -1;
	}

	//move pch to the position after ":"
	pch += strlen(headerName);
	//skip some spaces before the digits start
	while (isspace(*pch)){
		pch ++;
	}
	unsigned int ret = 0;
	while ( isdigit(*pch) ){

		ret = ret*10 + (*pch - '0');
		pch ++;
	}
	return ret;
}


string HttpHelper::extractPath(string url)
{
	//url is something like:	http://64.236.34.196:80/stream/1020

	string HttpScheme = "http://";
	int pathPartLength = 0;
	size_t startOfScheme = url.find(HttpScheme);

	if (string::npos == startOfScheme) {
		return "";
	}

	// then find the next "/" after "://"
	size_t host_path_seperator_pos = url.find("/",
			startOfScheme + HttpScheme.length());
	if (host_path_seperator_pos != string::npos) {
		pathPartLength = url.length() - (host_path_seperator_pos );
		return url.substr(host_path_seperator_pos, pathPartLength);
	}

	return "/";

}

//given an HTTP URL, extract the host part
string HttpHelper::extractHost(string url) {

	//url is something like:	http://64.236.34.196:80/stream/1020

	string HttpScheme = "http://";
	int hostPartLength = 0;
	size_t startOfScheme = url.find(HttpScheme);

	if (string::npos == startOfScheme) {
		return "";
	}

	// try to find the ":" which separates host and port
	size_t host_port_seperator_pos = url.find(":",
			startOfScheme + HttpScheme.length());
	if (host_port_seperator_pos != string::npos) {
		// host is the part between endOfScheme and host_port_seperator
		hostPartLength = host_port_seperator_pos
				- (startOfScheme + HttpScheme.length());
		return url.substr(startOfScheme + HttpScheme.length(), hostPartLength);
	}

	//see if there're path after the host and part part. path starts with a "/"
	size_t host_path_seperator_pos = url.find("/",
			startOfScheme + HttpScheme.length());
	if (host_path_seperator_pos != string::npos) {
		hostPartLength = host_path_seperator_pos
				- (startOfScheme + HttpScheme.length());
	} else {
		hostPartLength = url.length() - HttpScheme.length();
	}

	return url.substr(startOfScheme + HttpScheme.length(), hostPartLength);

}

//given an HTTP URL, extract the port
int HttpHelper::extractPort(string url) {

	//url is something like:	http://69.166.45.47:8000/

	string HttpScheme = "http://";
	int portPartLength = 0;
	size_t startOfScheme = url.find(HttpScheme);

	if (string::npos == startOfScheme) {
		//this is not HTTP scheme, return -1;
		return -1;
	}

	// try to find the ":" which separates host and port
	size_t host_port_seperator_pos = url.find(":",
			startOfScheme + HttpScheme.length());
	if (host_port_seperator_pos == string::npos) {
		// if there is no ":" after host, it means the port is default one.
		return 80;
	}

	//see if there're path after the host and part part. path starts with a "/"
	size_t host_path_seperator_pos = url.find("/",
			startOfScheme + HttpScheme.length());
	if (host_path_seperator_pos != string::npos) {
		portPartLength = host_path_seperator_pos
				- (host_port_seperator_pos + 1);
	} else {
		portPartLength = url.length() - (host_port_seperator_pos + 1);
	}

	string portStr = url.substr(host_port_seperator_pos + 1, portPartLength);

	return atoi(portStr.c_str());

}

/*
 * sending out an HTTP request and return the HttpResponse
 * This function is not suitable for "streaming" type of http transactions where the response will "never" come to an end
 */
void HttpHelper::retrieveURIContent(HttpRequest request,
		HttpResponse & response) {

	// first, lets get the content ready
	string httpRequestStr = createHttpRequestStr(request);
	logger->debug("httpRequest to send:\n%s", httpRequestStr.c_str());

	// now, let's connect to the remote server and send our request
	int clientSocket = create_tcp_socket();

	struct sockaddr_in svrSocketAddr; // the socket address of the server

	/* clear and initialize socketaddr */
	bzero((char *) &svrSocketAddr, sizeof(struct sockaddr_in));
	svrSocketAddr.sin_family = AF_INET;
	svrSocketAddr.sin_port = htons(request.remoteHostPort);

	struct hostent *srvHostEntry = gethostbyname(
			request.remoteHostName.c_str());
	if (NULL == srvHostEntry) {
		logger->error("Can't get server's address\n");
		perror("Can't get server's address\n");
		exit(1);
	}

	// now we have server's IP address in the 32 bits format
	bcopy(srvHostEntry->h_addr_list[0], &(svrSocketAddr.sin_addr),
			sizeof(struct in_addr));
	char * srv_ascii_ip = inet_ntoa(svrSocketAddr.sin_addr);
	logger->debug("host: \"%s\" is resolved to %s",
			request.remoteHostName.c_str(), srv_ascii_ip);

	/*connecting to the server*/
	int connResult = connect(clientSocket, (struct sockaddr *) &svrSocketAddr,
			sizeof(svrSocketAddr));
	if (-1 == connResult) {
		string msg = "failed to connect server";
		logger->error(msg.c_str());
		perror(msg.c_str());
		exit(1);
	}

	logger->debug("Connected: server's official host name is %s",
			srvHostEntry->h_name);

	// now let's send out packet to remote server
	send(clientSocket, httpRequestStr.c_str(), httpRequestStr.length(), 0);
	logger->debug("sending out HTTP request:\n%s", httpRequestStr.c_str());

	int totalSize = 0;
	memset(_httpReponseBuffer, 0, sizeof(_httpReponseBuffer));

	/* read the socket until its clear then exit. Save the response to httpReponseBuffer */
	char buffer[1024];
//	bzero(buffer, sizeof(buffer));
	int bufsize = read(clientSocket, buffer, sizeof(buffer));
	while ((0 != bufsize) && (-1 != bufsize)) {

		//copy this buffer to httpReponseBuffer
		memcpy(_httpReponseBuffer + totalSize, buffer, bufsize);

		totalSize += bufsize;

//		bzero(buffer, sizeof(buffer));
		bufsize = read(clientSocket, buffer, sizeof(buffer));
	}

	close(clientSocket);
	logger->debug("totally read : %d bytes", totalSize);

//	logger->debug("HTTP response received:================\n");
//	logger->debug(_httpReponseBuffer);

	response.data = _httpReponseBuffer;
}

string HttpHelper::createHttpRequestStr(HttpRequest httpRequest, bool useIcyMeta) {

	//GET / HTTP/1.1
	//User-Agent: nmps-radio-streamer
	//Host: www.shoutcast.com
	//Accept: */*

//	GET / HTTP/1.1
//	Host: 69.166.45.47:8000
//	Connection: close
//	icy-metadata: 1
//	transferMode.dlna.org: Streaming

	stringstream oss;
	string mystr;

	oss << "GET " << httpRequest.URI << " HTTP/1.0" << CRLF;
	oss << "User-Agent: nmps-radio-streamer" << CRLF;
	oss << "Accept: */*" << CRLF;
	oss << "Host: " << httpRequest.remoteHostName << CRLF;
	oss << "Connection: close" << CRLF;
	if (useIcyMeta){
		oss << "icy-metadata: 1" << CRLF;
		oss << "transferMode.dlna.org: Streaming" << CRLF;
	}
	oss << CRLF; // need an empty line at the end

	return oss.str();

}



